package org.skyui.accesscontrol;

import java.math.BigInteger;
import java.security.MessageDigest;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.StringTokenizer;

import org.skyui.beanmanagers.BeanSetManager;
import org.skyui.beans.NewsBean;
import org.skyui.beans.OrganizationBean;
import org.skyui.core.UIApplication;

import com.baycrypt.server.beans.people.UserBean;
import com.baycrypt.server.utils.BayCryptUtils;
import com.baycrypt.vaadinui.VaadinUIApplication;

public class HumanAuthenticator {

    private static Map<String, String> loggedOrganizations = new HashMap<String, String>();

    public static UserBean authenticateUser(String login, String password,
	    String productEdition) {
	if (login == null || password == null)
	    return null;
	Set<UserBean> users = BeanSetManager.getRefreshedBeans(UserBean.class,
		null);
	for (UserBean user : users) {
	    if (user.getEmail().equalsIgnoreCase(login)
		    && verifyPassword(password, user.getPasswordHash())
		    && user.getOrganization().getProductEdition()
			    .equals(productEdition)) {
		synchronized (loggedOrganizations) {
		    String username;
		    if (user.isAdmin()
			    && (username = loggedOrganizations.get(user
				    .getOrganization().getId())) != null
			    && !username.equals(login)) {
			throw new IllegalArgumentException(
				"Another admin is logged in. Please wait.");
		    } else {
			if (user.isAdmin())
			    loggedOrganizations.put(user.getOrganization()
				    .getId(), login);
		    }
		}
		UIApplication.getInstance().setUser(user);
		new NewsBean(user, null, null, "UI", "LOGIN", "logged in",
			NewsBean.PRIORITY_NORMAL, user.getOrganization())
			.createPersist();
		return user;

	    }
	}
	try {
	    Thread.sleep(100);
	} catch (InterruptedException e) {

	}
	return null;
    }

    public static void deauthenticateUser(UserBean user) {
	if (user == null)
	    throw new IllegalArgumentException("Null user");
	loggedOrganizations.remove(user.getOrganization().getId());
    }

    public static String passwordHash(String password, String salt) {
	MessageDigest sha;
	try {
	    sha = MessageDigest.getInstance("SHA");
	} catch (Exception e) {
	    throw new RuntimeException(e);
	}
	byte[] tmp = password.getBytes();
	for (int i = 0; i < 100; i++) {
	    sha.update(tmp);
	    sha.update((byte) Math.cos(i)); // make it non-standard
	    sha.update(salt.getBytes());
	    tmp = sha.digest();
	    sha.reset();
	}
	String hash = BayCryptUtils.removeFrontMinus(new BigInteger(tmp)
		.toString(16));
	StringBuffer sb = new StringBuffer(hash);
	sb.append(':');
	sb.append(salt);
	return sb.toString();
    }

    public static boolean verifyPassword(String password, String pwdHash) {
	StringTokenizer st = new StringTokenizer(pwdHash, ":");
	st.nextToken();
	String salt = st.nextToken();
	return pwdHash.equals(passwordHash(password, salt));
    }

    public static OrganizationBean getOrganization() {
	UIApplication application = VaadinUIApplication.getInstance();
	if (application != null && application.getOrganization() != null)
	    return application.getOrganization();
	throw new Error("No session exists");
    }

}
